package com.czy.util;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * @author chenzy
 * @since 2021-12-16
 * 对Optional的扩展，因为Optional为filnally无法继承
 **/
public class OP<T> {
    private static final OP<?> EMPTY = new OP<>();

    private final T value;


    private OP() {
        this.value = null;
    }

    public static <T> OP<T> empty() {
        //noinspection unchecked
        return (OP<T>) EMPTY;
    }

    private OP(T value) {
        this.value = value;
    }

    public static <T> OP<T> of(T value) {
        return new OP<>(value);
    }

    public static <T> OP<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    /** @noinspection OptionalUsedAsFieldOrParameterType*/
    public static <T> OP<T> transfer(Optional<T> optional) {
        return optional.map(OP::of).orElseGet(OP::empty);
    }

    public Optional<T> transfer() {
        return Optional.ofNullable(value);
    }

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
    public T getMayNull(){
        return value;
    }
    public boolean isPresent() {
        return value != null;
    }

    public boolean isEmpty() {
        return value == null;
    }


    public void ifPresent(Consumer<? super T> action) {
        if (value != null) {
            action.accept(value);
        }
    }

    public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
        if (value == null) {
            emptyAction.run();
        } else {
            action.accept(value);
        }
    }

    public <X extends Throwable> void ifPresentOrElseThrow(Consumer<? super T> action, Supplier<? extends X> exceptionSupplier) throws X {
        if (value == null) {
            throw exceptionSupplier.get();
        } else {
            action.accept(value);
        }
    }

    public OP<T> filter(Predicate<? super T> predicate) {
        return value == null || predicate.test(value) ? this : empty();
    }

    public T ifGet(Consumer<? super T> action) {
        if (value != null) {
            action.accept(value);
        }
        return value;
    }
    /**
     * 值不为空时执行，返回自身
     */
    public OP<T> ifDo(Consumer<? super T> action) {
        if (value != null) {
            action.accept(value);
        }
        return this;
    }
    public OP<T> ifDo(Predicate<? super T> filter,Consumer<? super T> action){
        if (value==null){
            return this;
        }
        if (filter.test(value)){
            action.accept(value);
        }
        return this;
    }
    /**
     */
    public OP<T> ifDoElseDo(Consumer<? super T> action,Runnable emptyAction) {
        if (value == null) {
            emptyAction.run();
        }else {
            action.accept(value);
        }
        return this;
    }
    public OP<T> ifDoElseDO(Predicate<? super T> filter,Consumer<? super T> action,Consumer<? super T> elseAction){
        if (value==null){
            return this;
        }
        if (filter.test(value)){
            action.accept(value);
        }else {
            elseAction.accept(value);
        }
        return this;
    }
    public <X extends RuntimeException> OP<T> ifThrow(Predicate<T> filter,Function<T,? extends X> exception){
        if (value==null){
            return this;
        }
        if (filter.test(value)){
            throw exception.apply(value);
        }
        return this;
    }

    public OP<T> anyDo(Runnable action) {
        action.run();
        return this;
    }


    /**
     * 值为空时执行，返回自身
     */
    public OP<T> elseDo(Runnable action) {
        if (value == null) {
            action.run();
        }
        return this;
    }

    public OP<T> ifOrElseGet(Consumer<? super T> action, Supplier<? extends T> supplier) {
        if (value == null) {
            return OP.ofNullable(supplier.get());
        }
        action.accept(value);
        return this;
    }

    public <U> U ifOrElseGet(Function<? super T, ? extends U> mapper, Supplier<? extends U> supplier) {
        if (value == null) {
            return supplier.get();
        }
        return mapper.apply(value);
    }

    public OP<T> removeIf(Predicate<? super T> filter) {
        return value == null||filter.test(value)?OP.empty():this;
    }

    public <U> OP<U> map(Function<? super T, ? extends U> mapper) {
        return value == null ? empty() : OP.ofNullable(mapper.apply(value));
    }

    public <U> OP<U> mapOrElse(Function<? super T, ? extends U> mapper, Supplier<U> supplier) {
        return OP.ofNullable(value == null ? supplier.get() : mapper.apply(value));
    }

    public <U> OP<U> flatMap(Function<? super T, ? extends OP<U>> mapper) {
        return value == null ? empty() : mapper.apply(value);
    }

    public <U> OP<U> flatMapOrElse(Function<? super T, ? extends OP<U>> mapper, Supplier<? extends OP<U>> supplier) {
        return value == null ? supplier.get() : mapper.apply(value);
    }

    public <U> OP<U> flatMapOrPackingElse(Function<? super T, ? extends OP<U>> mapper, Supplier<U> supplier) {
        return value == null ? OP.ofNullable(supplier.get()) : mapper.apply(value);
    }

    public OP<T> or(Supplier<? extends OP<T>> supplier) {
        return value == null ? supplier.get() : this;
    }

    public OP<T> orO(Supplier<? extends Optional<T>> supplier) {
        return value == null ? OP.transfer(supplier.get()) : this;
    }

    public OP<T> packingOr(Supplier<T> supplier) {
        return value == null ? OP.ofNullable(supplier.get()) : this;
    }

    public Stream<T> stream() {
        return value == null ? Stream.empty() : Stream.of(value);
    }

    public T orElse(T other) {
        return value != null ? value : other;
    }

    public T orElseGet(Supplier<? extends T> supplier) {
        return value != null ? value : supplier.get();
    }

    public T orElseThrow() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public <X extends RuntimeException> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    public <X extends RuntimeException> T emptyThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof OP<?> other)) {
            return false;
        }

        return Objects.equals(value, other.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value == null?"":value.toString();
    }
}
